package com.habdqn.attendance.dao;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.io.IOException;
import java.sql.*;
import java.util.List;
import java.util.Properties;

/**
 * 数据库操作基类：复用
 * 添加数据源配置
 *
 * @author Xing Jiejian 2019-08-14
 */
public abstract class BaseDao {

    private static HikariDataSource ds;


    private Connection conn;
    private PreparedStatement ps;
    private ResultSet rs;


    static {
        // 下面两个对象全局只需要唯一一个，可以通过单例模式配置
        Properties properties = new Properties();
        try {
            properties.load(BaseDao.class.getClassLoader().getResourceAsStream("hikari.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        HikariConfig config = new HikariConfig(properties);
        ds = new HikariDataSource(config);
    }


    /**
     * 公共的执行增删改的方法
     *
     * @param sql    带"?"占位符的SQL
     * @param params 占位符对应的参数
     * @return 影响的行数
     */
    protected int executeUpdate(String sql, Object... params) {
        try (Connection conn = this.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 公共的新增方法，返回新增对象主键
     * 针对可以有自增字段的数据库
     *
     * @param sql    带"?"占位符的SQL
     * @param params 占位符对应的参数
     * @return 新增对象的主键
     */
    protected int executeInsert(String sql, Object... params) {
        int generatedKey = -1;
        try (Connection conn = this.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            ps.executeUpdate();
            try (ResultSet rs = ps.getGeneratedKeys()) {
                while (rs.next()) {
                    generatedKey = rs.getInt(1);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return generatedKey;
    }

    /**
     * 公共的执行增删改的方法-可以增加事务处理
     *
     * @param connection 连接对象
     * @param sql        带"?"占位符的SQL
     * @param params     占位符对应的参数
     * @return 执行影响的行数
     */
    protected int executeUpdate(Connection connection, String sql, Object... params) {
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 公共的批量执行增删改的方法
     *
     * @param sql        带"?"占位符的SQL
     * @param paramsList 批量插入参数的数组集合
     */
    protected void executeBatch(String sql, List<Object[]> paramsList) {
        try {
            this.getConnection();
            ps = conn.prepareStatement(sql);
            //关闭自动提交
            conn.setAutoCommit(false);
            if (paramsList != null) {
                for (Object[] params : paramsList) {
                    for (int i = 0; i < params.length; i++) {
                        ps.setObject(i + 1, params[i]);
                    }
                    ps.addBatch();
                }
            }
            ps.executeBatch();
            conn.commit();
        } catch (SQLException e) {
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            closeAll();
        }
    }

    /**
     * 公共的查询的方法
     *
     * @param sql 带"?"占位符的SQL
     * @param params 参数数组
     * @return 结果集对象，给对应的调用方法自己解析结果集
     */
    protected ResultSet executeQuery(String sql, Object... params) {
        try {
            //获取连接
            this.getConnection();
            //执行SQL
            ps = conn.prepareStatement(sql);
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            rs = ps.executeQuery();
            return rs;
        } catch (SQLException e) {
            e.printStackTrace();
            closeAll();
        }
        return null;
    }

    /**
     * 获取唯一结果的方法
     * @param sql 带"?"占位符的SQL
     * @param params 参数数组
     * @return 返回SQL的唯一结果
     */
    protected Object uniqueResult(String sql, Object... params) {
        Object result = null;
        try {
            this.executeQuery(sql, params);
            while (rs.next()) {
                result = rs.getObject(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeAll();
        }
        return result;
    }

    /**
     * 判断查询结果集中是否存在某列
     *
     * @param rs         查询结果集
     * @param columnName 列名
     * @return true 存在; false 不存咋
     */
    protected boolean isExistColumn(ResultSet rs, String columnName) {
        try {
            if (rs.findColumn(columnName) > 0) {
                return true;
            }
        } catch (SQLException e) {
            return false;
        }
        return false;
    }

    /**
     * 通过数据源获取连接
     * @return 数据库连接对象
     * @throws SQLException 连接相关异常
     */
    private Connection getConnection() throws SQLException {
        conn = ds.getConnection();
        return conn;
    }

    /**
     * 静态工具方法，用来获取数据库连接
     *
     * @return 数据库连接对象
     * @throws SQLException 连接相关异常
     */
    public static Connection getConn() throws SQLException {
        return ds.getConnection();
    }


    /**
     * 公共的关闭所有资源的方法
     */
    protected void closeAll() {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
